﻿using System.Collections;
using UnityEngine;
using Yoozoo.Managers.ResourceManagerV2.Runtime;

namespace Gameplay.PVE
{
    public class StreamLod3Mesh : MonoBehaviour
    {
        public string MeshData1Path;
        public string MeshData2Path;
        public LODGroup lodGroup;
        LOD[] lods;
        bool existLod0 = false;
        bool existLod1 = false;
        private bool loading = false;

        public Renderer[] rendererLods0;
        public Renderer[] rendererLods1;
        public Renderer[] rendererLods2;
        public Renderer[] rendererLods0_1;
        public Renderer[] rendererLods0_2;
        public Renderer[] rendererLods1_2;
        public MeshFilter[] meshFiltersLods0;
        public MeshFilter[] meshFiltersLods1;
        public MeshFilter[] meshFiltersLods2;
        private MeshData meshData1;
        private MeshData meshData2;
        private bool isVisible = false;
        private int lod = 0;
        private bool isInitialized = false;
        private bool isLod2Enable = true;

        public bool CanTurnOffLod2 = false;

        void Start()
        {
            lods = lodGroup.GetLODs();
            lods[0].renderers = rendererLods0_2;
            lods[1].renderers = rendererLods1_2;

            int length = rendererLods0.Length;
            for (int i = 0, len = length; i < len; i++)
            {
                if (meshFiltersLods0[i])
                    meshFiltersLods0[i].sharedMesh = null;
            }

            length = rendererLods1.Length;
            for (int i = 0, len = length; i < len; i++)
            {
                if (meshFiltersLods1[i])
                    meshFiltersLods1[i].sharedMesh = null;
            }

            lodGroup.SetLODs(lods);

            lod = -1;

            // 物件初始创建在玩家面前的时候会先展示LOD2，然后再变成LOD1
            // 这里先关闭LOD2，等LOD1加载出来再显示 （效果也有问题，以后优化）
            // 但是道路有问题，明明离你很近却被判断为LOD2，于是道路就没了
            if(CanTurnOffLod2)
                TurnOffLod2();

            StartCoroutine(Loop());
        }

        private int GetCurrentLod()
        {
            if (!isVisible)
            {
                // 摄像机没有拍到的情况，不要改lod级别
                return lod;
            }

            int level = lod;
            if (rendererLods0[0].isVisible && lod != 0)
            {
                level = 0;
            }
            else if (rendererLods1[0].isVisible && lod != 1)
            {
                level = 1;
            }
            else if (rendererLods2[0].isVisible && lod != 2)
            {
                level = 2;
            }

            return level;
        }

        public void Setup()
        {
            lodGroup = GetComponent<LODGroup>();
            lods = lodGroup.GetLODs();

            rendererLods0 = lods[0].renderers;
            rendererLods1 = lods[1].renderers;
            rendererLods2 = lods[2].renderers;

            rendererLods0_1 = new Renderer[rendererLods0.Length + rendererLods1.Length];
            rendererLods0.CopyTo(rendererLods0_1, 0);
            rendererLods1.CopyTo(rendererLods0_1, rendererLods0.Length);

            rendererLods0_2 = new Renderer[rendererLods0.Length + rendererLods2.Length];
            rendererLods0.CopyTo(rendererLods0_2, 0);
            rendererLods2.CopyTo(rendererLods0_2, rendererLods0.Length);

            rendererLods1_2 = new Renderer[rendererLods1.Length + rendererLods2.Length];
            rendererLods1.CopyTo(rendererLods1_2, 0);
            rendererLods2.CopyTo(rendererLods1_2, rendererLods1.Length);

            meshFiltersLods0 = new MeshFilter[rendererLods0.Length];
            meshFiltersLods1 = new MeshFilter[rendererLods1.Length];
            meshFiltersLods2 = new MeshFilter[rendererLods2.Length];

            for (int i = 0, len = rendererLods0.Length; i < len; i++)
            {
                var renderer1 = rendererLods0[i];
                if (renderer1)
                    meshFiltersLods0[i] = renderer1.GetComponent<MeshFilter>();
            }

            for (int i = 0, len = rendererLods1.Length; i < len; i++)
            {
                var renderer1 = rendererLods1[i];
                if (renderer1)
                    meshFiltersLods1[i] = renderer1.GetComponent<MeshFilter>();
            }

            for (int i = 0, len = rendererLods2.Length; i < len; i++)
            {
                var renderer1 = rendererLods2[i];
                if (renderer1)
                    meshFiltersLods2[i] = renderer1.GetComponent<MeshFilter>();
            }

            int length = rendererLods0.Length;
            for (int i = 0, len = length; i < len; i++)
            {
                if (meshFiltersLods0[i])
                    meshFiltersLods0[i].sharedMesh = null;
            }

            length = rendererLods1.Length;
            for (int i = 0, len = length; i < len; i++)
            {
                if (meshFiltersLods1[i])
                    meshFiltersLods1[i].sharedMesh = null;
            }
        }

        IEnumerator Loop()
        {
            float stepTime = 1f;
            var waitForSeconds = new WaitForSeconds(stepTime);

            while (true)
            {
                yield return waitForSeconds;

                //if (Camera.current == null)
                //{
                //    yield return 0;
                //    continue;
                //}

                //float dis = Vector3.Distance(Camera.current.transform.position, transform.position);
                //stepTime = Mathf.Clamp(dis * 0.01f, 0.05f, 10);

                CheckVisible();

                if (loading || !isVisible)
                {
                    // 还在加载中，不要改LOD
                    // 摄像机没有拍到的时候，不要改LOD
                }
                else
                {
                    int newLod = GetCurrentLod();
                    if (newLod != lod)
                    {
                        if (newLod == 0)
                        {
                            if (existLod0)
                            {
                                SetLod0();
                            }
                            else
                            {
                                LoadLod0();
                            }
                        }
                        else if (newLod == 1)
                        {
                            if (existLod1)
                            {
                                SetLod1();
                            }
                            else
                            {
                                LoadLod1();
                            }
                        }
                        else if (newLod == 2)
                        {
                            TurnOnLod2();
                            UnloadLod();
                        }
                    }
                }
            }
        }
        
        private void UnloadLod0()
        {
            existLod0 = false;
            for (int i = 0, len = meshFiltersLods0.Length; i < len; i++)
                meshFiltersLods0[i].sharedMesh = null;

            if (meshData1)
            {
                UMTResource.UnloadAssetImmediately(meshData1);
                meshData1 = null;
            }

            lods[0].renderers = existLod1 ? rendererLods0_1 : rendererLods0_2;
            
            lodGroup.SetLODs(lods);
        }

        private void UnloadLod1()
        {
            existLod1 = false;
            for (int i = 0, len = meshFiltersLods1.Length; i < len; i++)
                meshFiltersLods1[i].sharedMesh = null;

            if (meshData2)
            {
                UMTResource.UnloadAssetImmediately(meshData2);
                meshData2 = null;
            }

            lods[1].renderers = rendererLods1_2;
            lodGroup.SetLODs(lods);
        }

        private void LoadLod0()
        {
            loading = true;
            UMTResource.LoadAssetRapid(MeshData1Path, (assetName, asset, duration, data) =>
            {
                if (gameObject)
                {
                    loading = false;
                    meshData1 = asset as MeshData;
                    existLod0 = true;
                    SetLod0();
                }
                else
                {
                    UMTResource.UnloadAssetImmediately(asset);
                }
            });

            if (!existLod1)
            {
                UMTResource.LoadAssetRapid(MeshData2Path, (assetName, asset, duration, data) =>
                {
                    if (gameObject)
                    {
                        meshData2 = asset as MeshData;
                        var meshes = meshData2.lodMeshes;

                        int length = Mathf.Min(meshFiltersLods1.Length, meshes.Length);
                        for (int i = 0; i < length; i++)
                        {
                            if (meshFiltersLods1[i])
                                meshFiltersLods1[i].sharedMesh = meshes[i];
                        }

                        int currentLod = GetCurrentLod();
                        if (currentLod == 0)
                        {
                            lods[1].renderers = rendererLods0_1;
                            lodGroup.SetLODs(lods);
                        }

                        existLod1 = true;
                    }
                    else
                    {
                        UMTResource.UnloadAssetImmediately(asset);
                    }
                });
            }
        }

        private void SetLod0()
        {
            if (meshData1 == null) return;

            var meshes = meshData1.lodMeshes;

            int length = Mathf.Min(meshFiltersLods0.Length, meshes.Length);
            for (int i = 0; i < length; i++)
            {
                if (meshFiltersLods0[i])
                    meshFiltersLods0[i].sharedMesh = meshes[i];
            }

            lods[0].renderers = rendererLods0;
            lodGroup.SetLODs(lods);
            UnloadLod();
            TurnOnLod2();
        }

        private void LoadLod1()
        {
            loading = true;
            UMTResource.LoadAssetRapid(MeshData2Path, (assetName, asset, duration, data) =>
            {
                if (gameObject)
                {
                    loading = false;
                    meshData2 = asset as MeshData;
                    existLod1 = true;
                    SetLod1();
                }
                else
                {
                    UMTResource.UnloadAssetImmediately(asset);
                }
            });

            if (!existLod0)
            {
                UMTResource.LoadAssetRapid(MeshData1Path, (assetName, asset, duration, data) =>
                {
                    if (gameObject)
                    {
                        meshData1 = asset as MeshData;
                        var meshes = meshData1.lodMeshes;

                        int length = Mathf.Min(meshFiltersLods1.Length, meshes.Length);
                        for (int i = 0; i < length; i++)
                        {
                            if (meshFiltersLods1[i])
                                meshFiltersLods1[i].sharedMesh = meshes[i];
                        }

                        int currentLod = GetCurrentLod();
                        if (currentLod == 1)
                        {
                            lods[0].renderers = rendererLods0_1;
                            lodGroup.SetLODs(lods);
                        }

                        existLod0 = true;
                    }
                    else
                    {
                        UMTResource.UnloadAssetImmediately(asset);
                    }
                });
            }
        }

        private void SetLod1()
        {
            if (meshData2 == null) return;

            var meshes = meshData2.lodMeshes;

            int length = Mathf.Min(meshFiltersLods1.Length, meshes.Length);
            for (int i = 0; i < length; i++)
            {
                if (meshFiltersLods1[i])
                    meshFiltersLods1[i].sharedMesh = meshes[i];
            }

            lods[1].renderers = rendererLods1;
            lodGroup.SetLODs(lods);
            UnloadLod(); 
            TurnOnLod2();
        }

        private void UnloadLod()
        {
            lod = GetCurrentLod();

            // LOD0总是卸载
            if (lod != 0 && existLod0)
            {
                UnloadLod0();
            }

            // 如果当前是LOD2，那么卸载LOD1，如果当前是LOD0，那么就不卸载
            if (lod == 2 && existLod1)
            {
                UnloadLod1();
            }
        }

        private void CheckVisible()
        {
            isVisible = rendererLods0[0].isVisible || rendererLods1[0].isVisible || rendererLods2[0].isVisible;

            if (isVisible)
            {
                if (!isInitialized)
                {
                    // 物件第一次可见时，如果LOD不是2，那么先关闭LOD2，等LOD0或者LOD1加载完成再显示出来
                    isInitialized = true;
                    if (!rendererLods2[0].isVisible)
                    {
                        TurnOffLod2();
                    }
                }
            }
        }

        private void TurnOffLod2()
        {
            if (isLod2Enable)
            {
                foreach (var renderer1 in rendererLods2)
                {
                    renderer1.enabled = false;
                }

                isLod2Enable = false;
            }
        }

        private void TurnOnLod2()
        {
            if (!isLod2Enable)
            {
                foreach (var renderer1 in rendererLods2)
                {
                    renderer1.enabled = true;
                }

                isLod2Enable = true;
            }
        }

        void OnDestroy()
        {
            if (meshData1)
            {
                UMTResource.UnloadAssetImmediately(meshData1);
                meshData1 = null;
            }

            if (meshData2)
            {
                UMTResource.UnloadAssetImmediately(meshData2);
                meshData2 = null;
            }
        }
    }
}
